import streamlit as st
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
from wordcloud import WordCloud
from zhipuai import ZhipuAI
import json
import re
import os
from pyecharts.charts import Bar, Pie, Line
from pyecharts import options as opts
from streamlit_echarts import st_pyecharts
from mlxtend.preprocessing import TransactionEncoder
from mlxtend.frequent_patterns import apriori, association_rules
import ast
from collections import defaultdict
import hashlib
import time
import logging
import gc

# ==================== 初始化设置 ====================
st.set_page_config(
    page_title="招聘数据分析与职业规划助手",
    layout="wide",
    initial_sidebar_state="expanded"
)

# 设置中文字体
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False
sns.set(font='SimHei')

# 配置日志
logging.basicConfig(level=logging.WARNING)
logger = logging.getLogger(__name__)
logging.getLogger("httpx").setLevel(logging.WARNING)

# ==================== 数据加载和预处理 ====================
@st.cache_data
def load_data():
    try:
        df = pd.read_csv("招聘数据集(含技能列表）.csv", encoding='utf-8')
        
        # 薪资处理
        def parse_salary(salary_str):
            try:
                if pd.isna(salary_str) or not isinstance(salary_str, str):
                    return 0
                
                if 'k' in salary_str.lower():
                    nums = [float(num.replace('k', '').strip()) 
                           for num in re.split(r'[-~—]', salary_str) if num.strip()]
                    return sum(nums) * 1000 / len(nums) if nums else 0
                return float(salary_str) if salary_str.strip() else 0
            except:
                return 0
        
        df['salary'] = df['salary'].apply(parse_salary)
        
        # 技能标签处理 
        if 'skillLables' in df.columns:
            # 确保所有值为字符串，处理NaN和float值
            df['skillLables'] = df['skillLables'].fillna('').astype(str)
            # 进一步处理可能的float值
            df['skillLables'] = df['skillLables'].apply(
                lambda x: [str(s).strip() for s in x.split(',') if str(s).strip()] 
                if isinstance(x, str) else []
            )
            # 展开技能列表
            df = df.explode('skillLables').reset_index(drop=True)
            df['skillLables'] = df['skillLables'].str.strip()
            df['skill_list'] = df.groupby(['positionName', 'companyFullName', 'city'])['skillLables'].transform(
                lambda x: ','.join([str(s) for s in x if str(s).strip()])
            )
        
        # 时间处理
        if 'createTime' in df.columns:
            df['createTime'] = pd.to_datetime(df['createTime'], errors='coerce')
            df['createHour'] = df['createTime'].dt.hour
        
        return df.dropna(subset=['salary'])  # 移除无效薪资记录
    except Exception as e:
        st.error(f"数据加载失败: {str(e)}")
        return pd.DataFrame()

# ==================== 用户认证系统 ====================
def make_hashes(password):
    return hashlib.sha256(str.encode(password)).hexdigest()

def check_hashes(password, hashed_text):
    return make_hashes(password) == hashed_text

def login_page():
    # 自定义CSS样式 - 纯色背景版
    st.markdown("""
    <style>
    .login-container {
        max-width: 500px;
        padding: 30px;
        margin: 100px auto;
        border-radius: 15px;
        box-shadow: 0 8px 16px rgba(0,0,0,0.1);
        background: white;
        animation: fadeIn 0.5s ease-in-out;
    }
    
    @keyframes fadeIn {
        from { opacity: 0; transform: translateY(20px); }
        to { opacity: 1; transform: translateY(0); }
    }
    
    .login-title {
        text-align: center;
        color: #2c3e50;
        margin-bottom: 30px;
        font-weight: 700;
        font-size: 28px;
    }
    
    .login-input {
        margin-bottom: 20px;
    }
    
    .login-button {
        width: 100%;
        padding: 12px;
        border-radius: 8px;
        font-weight: 600;
        background: #2c3e50;
        color: white;
        border: none;
        transition: all 0.3s;
    }
    
    .login-button:hover {
        background: #1a2636;
        transform: translateY(-2px);
        box-shadow: 0 5px 15px rgba(0,0,0,0.1);
    }
    
    .login-footer {
        text-align: center;
        margin-top: 20px;
        color: #7f8c8d;
        font-size: 14px;
    }
    
    .stApp {
        background: #f5f7fa;
    }
    
    .logo {
        text-align: center;
        margin-bottom: 20px;
    }
    
    .logo img {
        width: 80px;
        height: 80px;
        border-radius: 50%;
        object-fit: cover;
        box-shadow: 0 4px 8px rgba(0,0,0,0.1);
    }
    
    /* 输入框样式 */
    .stTextInput>div>div>input {
        border-radius: 8px;
        padding: 10px 15px;
        border: 1px solid #dfe6e9;
    }
    
    /* 密码输入框样式 */
    .stTextInput>div>div>input[type="password"] {
        font-family: monospace;
    }
    </style>
    """, unsafe_allow_html=True)

    # 登录状态检查
    if 'logged_in' not in st.session_state:
        st.session_state.logged_in = False
    
    if not st.session_state.logged_in:
        # 创建登录容器
        with st.container():
            st.markdown("""
            <div class="login-container">
                <div class="logo">
                    <img src="https://img.icons8.com/fluency/96/000000/linkedin.png" alt="Logo">
                </div>
                <div class="login-title">
                    招聘数据分析与职业规划助手
                </div>
            """, unsafe_allow_html=True)
            
            # 登录表单
            login_form = st.form(key="login_form")
            with login_form:
                username = st.text_input("用户名", placeholder="请输入您的用户名", key="username")
                password = st.text_input("密码", type="password", placeholder="请输入您的密码", key="password")
                
                col1, col2, col3 = st.columns([1,2,1])
                with col2:
                    submit = st.form_submit_button("登 录", use_container_width=True)
                
                st.markdown("""
                <div class="login-footer">
                    © 2023 招聘数据分析系统 | 版本 1.0.0
                </div>
                """, unsafe_allow_html=True)
            
            if submit:
                if username == "admin" and password == "admin123":
                    st.session_state.logged_in = True
                    st.session_state.user = username
                    st.success("登录成功!")
                    time.sleep(1)
                    st.rerun()
                else:
                    st.error("用户名或密码错误")
    
    if st.session_state.logged_in:
        # 用户已登录状态
        st.sidebar.markdown(f"""
        <div style="display: flex; align-items: center; margin-bottom: 20px;">
            <img src="https://img.icons8.com/fluency/48/000000/user-male-circle.png" style="margin-right: 10px;">
            <div>
                <h4 style="margin: 0; color: #2c3e50;">{st.session_state.user}</h4>
                <p style="margin: 0; font-size: 12px; color: #7f8c8d;">用户</p>
            </div>
        </div>
        """, unsafe_allow_html=True)
        
        if st.sidebar.button("退出登录", key="logout"):
            st.session_state.logged_in = False
            st.rerun()
        return True
    return False
# ==================== 职业规划工具 ====================
class CareerAdvisor:
    def __init__(self, df):
        self.df = df
        self.skill_position_map = defaultdict(list)# 技能→岗位列表的映射
        self.position_skill_map = defaultdict(list)# 岗位→技能列表的映射
        self.skill_association_rules = None# 技能关联规则
        self._build_mappings()# 构建初始映射
        self._generate_association_rules()# 生成关联规则
    
    def get_top_3_skills(self, position):  
        skills = self.get_skills_for_position(position, top_n=3)
        return ", ".join([skill for skill, _ in skills])

    def get_top_related(self, skill):
        """获取与指定技能最相关的3个技能"""
        related = self.get_related_skills(skill, top_n=3)
        return ", ".join([s for s, _ in related])

    def get_advanced_skills(self, position):
        """获取岗位的进阶技能"""
        skills = self.get_skills_for_position(position, top_n=10)
        if len(skills) > 3:
            return ", ".join([skill for skill, _ in skills[3:6]])
        return "暂无数据"
    def _build_mappings(self):
        #数据预处理​
        """构建技能与岗位的双向映射关系"""
        for _, row in self.df.iterrows():
            position = row['positionName']
            skills = []
            
            if pd.notna(row['skillLables']):
                try:
                    if isinstance(row['skillLables'], str):
                        skills = [s.strip().lower() for s in row['skillLables'].split(',') if s.strip()]
                    elif isinstance(row['skillLables'], list):
                        skills = [str(s).strip().lower() for s in row['skillLables'] if pd.notna(s)]
                except Exception as e:
                    logger.warning(f"解析skillLables失败: {str(e)}")
                    continue
            
            for skill in skills:
                self.skill_position_map[skill].append(position)
                self.position_skill_map[position].append(skill)
    
    def _generate_association_rules(self, min_support=0.3, min_threshold=1.5):
        #关联规则挖掘
        """生成技能关联规则"""
        skill_counts = defaultdict(int)
        for skills in self.position_skill_map.values():
            for skill in set(skills):
                skill_counts[skill] += 1
                
        frequent_skills = {k for k, v in skill_counts.items() if v >= 10}
        
        batch_size = 500
        positions = list(self.position_skill_map.keys())
        
        for i in range(0, len(positions), batch_size):
            batch_positions = positions[i:i+batch_size]
            batch_transactions = []
            
            for pos in batch_positions:
                skills = [s for s in self.position_skill_map[pos] if s in frequent_skills]
                if skills:
                    batch_transactions.append(list(set(skills)))
                    
            if batch_transactions:
                te = TransactionEncoder()
                te_ary = te.fit(batch_transactions).transform(batch_transactions)
                df = pd.DataFrame(te_ary, columns=te.columns_)
                
                frequent_itemsets = apriori(
                    df, 
                    min_support=min_support,
                    use_colnames=True,
                    low_memory=True,
                    max_len=3
                )
                
                if not frequent_itemsets.empty:
                    batch_rules = association_rules(
                        frequent_itemsets, 
                        metric="lift", 
                        min_threshold=min_threshold
                    )
                    if self.skill_association_rules is None:
                        self.skill_association_rules = batch_rules
                    else:
                        self.skill_association_rules = pd.concat(
                            [self.skill_association_rules, batch_rules]
                        ).drop_duplicates()
                
                del batch_transactions, te_ary, df
                gc.collect()
    
    def get_related_skills(self, skill, top_n=5):
        """获取与指定技能相关的其他技能"""
        skill = str(skill).lower().strip()
        related = defaultdict(float)
        
        if self.skill_association_rules is not None:
            for _, rule in self.skill_association_rules.iterrows():
                if skill in rule['antecedents']:
                    for s in rule['consequents']:
                        related[str(s).lower().strip()] = max(related[str(s).lower().strip()], rule['lift'])
                elif skill in rule['consequents']:
                    for s in rule['antecedents']:
                        related[str(s).lower().strip()] = max(related[str(s).lower().strip()], rule['lift'])
        
        if not related:
            # 如果没有关联规则，使用简单的共现统计
            for position in self.skill_position_map.get(skill, []):
                for s in self.position_skill_map.get(position, []):
                    s = str(s).lower().strip()
                    if s != skill:
                        related[s] += 1
        
        # 确保返回格式为[(技能, 值), ...]
        sorted_skills = sorted(related.items(), key=lambda x: -x[1])
        return [(s, v) for s, v in sorted_skills][:top_n]
    
    def get_skills_for_position(self, position, top_n=10):
        """获取指定岗位所需的技能"""
        position = str(position).lower().strip()
        skills = defaultdict(int)
        
        for pos, p_skills in self.position_skill_map.items():
            if position in pos.lower():
                for skill in p_skills:
                    skills[str(skill).lower().strip()] += 1
        
        # 确保返回格式为[(技能, 计数), ...]
        sorted_skills = sorted(skills.items(), key=lambda x: -x[1])
        return [(s, c) for s, c in sorted_skills][:top_n]
    
    def get_positions_for_skills(self, skills, top_n=5):
        """根据技能组合推荐岗位"""
        skill_set = {str(s).lower().strip() for s in skills}
        position_scores = defaultdict(float)
        
        for position, p_skills in self.position_skill_map.items():
            p_skill_set = {str(s).lower().strip() for s in p_skills}
            match = skill_set & p_skill_set
            position_scores[position] = len(match) / len(p_skill_set) if p_skill_set else 0
        
        # 确保返回格式为[(岗位, 分数), ...]
        sorted_positions = sorted(position_scores.items(), key=lambda x: x[1], reverse=True)
        return [(pos, score) for pos, score in sorted_positions if score > 0][:top_n]
    
    def query_skill_position_association(self, skill, position):
        """
        查询技能与岗位的关联性
        :param skill: 技能名称
        :param position: 岗位名称
        :return: 关联性分数（0-1之间）
        """
        skill = str(skill).lower().strip()
        position = str(position).lower().strip()
        
        # 检查岗位是否存在
        position_found = False
        for p in self.position_skill_map.keys():
            if position in p.lower():
                position_found = True
                position = p  # 使用标准化的岗位名称
                break
        
        if not position_found:
            return 0.0
        
        # 检查技能是否存在于该岗位的技能列表中
        p_skills = [str(s).lower().strip() for s in self.position_skill_map[position]]
        if skill in p_skills:
            return 1.0
        
        # 检查技能是否与岗位所需技能有关联
        related_skills = self.get_related_skills(skill)
        for s, score in related_skills:
            if s in p_skills:
                return min(1.0, score * 0.8)  # 关联技能给予一定权重
        
        return 0.0

    def query_skill_skill_association(self, skill1, skill2):
        """
        查询两个技能之间的关联性（基于关联规则）
        :param skill1: 技能1
        :param skill2: 技能2
        :return: 关联性分数（lift值，若无关联则返回0）
        """
        # 标准化技能名称
        skill1 = str(skill1).lower().strip()
        skill2 = str(skill2).lower().strip()
        
        # 如果两个技能相同，返回最高关联度
        if skill1 == skill2:
            return 1.0
        
        max_lift = 0.0
        if self.skill_association_rules is not None:
            for _, rule in self.skill_association_rules.iterrows():
                try:
                    # 标准化处理规则中的技能名称
                    antecedents = {str(s).lower().strip() for s in rule['antecedents']}
                    consequents = {str(s).lower().strip() for s in rule['consequents']}
                    
                    # 检查双向关联
                    if (skill1 in antecedents and skill2 in consequents) or \
                       (skill2 in antecedents and skill1 in consequents):
                        max_lift = max(max_lift, rule['lift'])
                    
                    # 检查同组关联（技能在同一规则的前件或后件中）
                    elif (skill1 in antecedents and skill2 in antecedents) or \
                         (skill1 in consequents and skill2 in consequents):
                        max_lift = max(max_lift, rule['lift'] * 0.8)  # 同组关联给予一定权重
                except Exception as e:
                    logger.warning(f"解析关联规则失败: {str(e)}")
                    continue
        
        # 如果没有找到关联规则，使用简单的共现统计
        if max_lift == 0.0:
            positions_with_skill1 = set(self.skill_position_map.get(skill1, []))
            positions_with_skill2 = set(self.skill_position_map.get(skill2, []))
            
            common_positions = positions_with_skill1 & positions_with_skill2
            total_positions = positions_with_skill1 | positions_with_skill2
            
            if total_positions:
                max_lift = len(common_positions) / len(total_positions)
        
        return max_lift

# ==================== ai智能助手 ====================
def get_asso_skill(dataset, skill, prompt, selected_charts=None, **kwargs):
   #查找与指定技能相关联的其他技能
    if selected_charts is None:
        selected_charts = ["柱状图", "饼图", "折线图", "表格"]
    
    skill = skill.lower()
    
    skill_count = {}
    for index, row in dataset.iterrows():
        if skill in row['skill_list'].lower():
            for other_skill in row['skill_list'].split(','):
                other_skill = other_skill.strip().lower()
                if other_skill != skill:
                    if other_skill in skill_count:
                        skill_count[other_skill] += 1
                    else:
                        skill_count[other_skill] = 1
    
    sorted_skills = sorted(skill_count.items(), key=lambda x: x[1], reverse=True)
    top_skills = dict(sorted_skills[:5])
    
    if len(top_skills) == 0:
        return prompt, None, None
    
    skills_str = ', '.join(top_skills.keys())
    charts = []
    
    if "柱状图" in selected_charts:
        bar = (
            Bar()
            .add_xaxis(list(top_skills.keys()))
            .add_yaxis("共同出现次数", list(top_skills.values()))
            .set_global_opts(
                title_opts=opts.TitleOpts(title=f"{skill}相关技能统计"),
                xaxis_opts=opts.AxisOpts(axislabel_opts=opts.LabelOpts(rotate=45))
            )
        )
        charts.append(bar)
    
    if "饼图" in selected_charts:
        pie = (
            Pie()
            .add(
                "",
                list(top_skills.items()),
                radius=["40%", "75%"],
            )
            .set_global_opts(
                title_opts=opts.TitleOpts(title=f"{skill}技能分布"),
                legend_opts=opts.LegendOpts(orient="vertical", pos_top="15%", pos_left="2%"),
            )
            .set_series_opts(
                label_opts=opts.LabelOpts(formatter="{b}: {c} ({d}%)")
            )
        )
        charts.append(pie)
    
    if "折线图" in selected_charts:
        line = (
            Line()
            .add_xaxis(list(top_skills.keys()))
            .add_yaxis("共同出现次数", list(top_skills.values()))
            .set_global_opts(
                title_opts=opts.TitleOpts(title=f"{skill}技能趋势"),
                xaxis_opts=opts.AxisOpts(axislabel_opts=opts.LabelOpts(rotate=45))
            )
        )
        charts.append(line)
    
    # 新增技能关联表格
    table_data = None
    if "表格" in selected_charts:
        table_data = pd.DataFrame({
            "关联技能": list(top_skills.keys()),
            "共同出现次数": list(top_skills.values())
        })
    
    return f"针对问题{skill},我查询到相关性最高的技术是{skills_str}，请将查询结果组织成人类语言描述。可以附加一些学习建议。总字数不超过200字", charts, table_data

def cacu_postion_skill_wordcount(dataset, postionName, count=15, selected_charts=None, **kwargs):
    #统计特定岗位的技能需求频率
    if selected_charts is None:
        selected_charts = ["柱状图", "饼图", "折线图", "表格"]
    
    postion_data = dataset[dataset['positionName'].str.contains(postionName, case=False)]
    skill_list = postion_data['skill_list'].tolist()
    
    word_count = {}
    for skills in skill_list:
        for skill in skills.split(','):
            skill = skill.strip().lower()
            if skill in word_count:
                word_count[skill] += 1
            else:
                word_count[skill] = 1
    
    sorted_skills = sorted(word_count.items(), key=lambda x: x[1], reverse=True)
    skills = dict(sorted_skills[:count])
    
    if len(skills) == 0:
        return kwargs.get('prompt', ''), None, None
    
    skills_str = ', '.join([f"{skill}({count})" for skill, count in skills.items()])
    charts = []
    
    if "柱状图" in selected_charts:
        bar = (
            Bar()
            .add_xaxis(list(skills.keys()))
            .add_yaxis("技能出现次数", list(skills.values()))
            .set_global_opts(
                title_opts=opts.TitleOpts(title=f"{postionName}岗位技能统计"),
                xaxis_opts=opts.AxisOpts(
                    axislabel_opts=opts.LabelOpts(rotate=45),
                    interval=0
                )
            )
        )
        charts.append(bar)
    
    if "饼图" in selected_charts:
        pie = (
            Pie()
            .add(
                "",
                list(skills.items()),
                radius=["40%", "75%"],
            )
            .set_global_opts(
                title_opts=opts.TitleOpts(title=f"{postionName}技能分布"),
                legend_opts=opts.LegendOpts(orient="vertical", pos_top="15%", pos_left="2%"),
            )
            .set_series_opts(
                label_opts=opts.LabelOpts(formatter="{b}: {c} ({d}%)")
            )
        )
        charts.append(pie)
    
    if "折线图" in selected_charts:
        line = (
            Line()
            .add_xaxis(list(skills.keys()))
            .add_yaxis("技能出现次数", list(skills.values()))
            .set_global_opts(
                title_opts=opts.TitleOpts(title=f"{postionName}技能趋势"),
                xaxis_opts=opts.AxisOpts(
                    axislabel_opts=opts.LabelOpts(rotate=45)
                )
            )
        )
        charts.append(line)
    
    # 新增岗位技能表格
    table_data = None
    if "表格" in selected_charts:
        table_data = pd.DataFrame({
            "技能名称": list(skills.keys()),
            "出现次数": list(skills.values())
        })
    
    return f"针对问题{postionName},我查询到最重要的技能是{skills_str}，请将查询结果组织成人类语言描述。可以附加一些学习建议。总字数不超过200字", charts, table_data

def cacu_skill_position_wordcount(dataset, skill, count=10, selected_charts=None, **kwargs):
    #查找需要特定技能的岗位
    if selected_charts is None:
        selected_charts = ["柱状图", "饼图", "折线图", "表格"]
    
    skill = skill.lower()
    position_count = {}
    
    for index, row in dataset.iterrows():
        if skill in row['skill_list'].lower():
            position = row['positionName']
            if position in position_count:
                position_count[position] += 1
            else:
                position_count[position] = 1
    
    sorted_positions = sorted(position_count.items(), key=lambda x: x[1], reverse=True)
    positions = dict(sorted_positions[:count])
    
    if len(positions) == 0:
        return kwargs.get('prompt', ''), None, None
    
    positions_str = ', '.join(positions.keys())
    charts = []
    
    if "柱状图" in selected_charts:
        bar = (
            Bar()
            .add_xaxis(list(positions.keys()))
            .add_yaxis("岗位出现次数", list(positions.values()))
            .set_global_opts(
                title_opts=opts.TitleOpts(title=f"{skill}技能岗位统计"),
                xaxis_opts=opts.AxisOpts(axislabel_opts=opts.LabelOpts(rotate=45))
            )
        )
        charts.append(bar)
    
    if "饼图" in selected_charts:
        pie = (
            Pie()
            .add(
                "",
                list(positions.items()),
                radius=["40%", "75%"],
                label_opts=opts.LabelOpts(formatter="{b}: {c} ({d}%)")
            )
            .set_global_opts(
                title_opts=opts.TitleOpts(title=f"{skill}技能岗位分布"),
                legend_opts=opts.LegendOpts(pos_left="right")
            )
        )
        charts.append(pie)
    
    if "折线图" in selected_charts:
        line = (
            Line()
            .add_xaxis(list(positions.keys()))
            .add_yaxis("岗位出现次数", list(positions.values()))
            .set_global_opts(
                title_opts=opts.TitleOpts(title=f"{skill}技能岗位趋势"),
                xaxis_opts=opts.AxisOpts(axislabel_opts=opts.LabelOpts(rotate=45))
            )
        )
        charts.append(line)
    
    # 新增技能岗位表格
    table_data = None
    if "表格" in selected_charts:
        table_data = pd.DataFrame({
            "岗位名称": list(positions.keys()),
            "出现次数": list(positions.values())
        })
    
    return f"针对问题{skill},我查询到关联性最高的岗位是{positions_str}，请将查询结果组织成人类语言描述。可以附加一些职业发展建议。总字数不超过200字", charts, table_data

def parse_salary(salary_str):
    #解析薪资字符串，计算平均薪资
    try:
        if not salary_str or not isinstance(salary_str, str):
            return 0
        
        ranges = []
        pattern = re.compile(r'(\d+k-\d+k)')
        matches = pattern.findall(salary_str)
        
        for match in matches:
            try:
                start, end = match.split('-')
                start = float(start.replace('k', ''))
                end = float(end.replace('k', ''))
                ranges.append((start, end))
            except ValueError:
                continue
        
        if not ranges:
            try:
                value = float(salary_str.strip().replace('k', ''))
                return value
            except ValueError:
                return 0
        
        total = 0
        count = 0
        for start, end in ranges:
            total += (start + end) / 2
            count += 1
        avg_salary = total / count if count > 0 else 0
        return avg_salary
    except Exception as e:
        return 0

def get_job_recommendations(dataset, skill, count=10, selected_charts=None, **kwargs):
    #获取与特定技能相关的岗位推荐
    if selected_charts is None:
        selected_charts = ["表格"]  # 默认只显示表格
    
    skill = skill.lower()
    job_data = dataset[dataset['skill_list'].str.lower().str.contains(skill)]
    
    if job_data.empty:
        return f"没有找到与{skill}相关的招聘岗位", None, None
    
    # 解析薪资
    job_data['parsed_salary'] = job_data['salary'].apply(parse_salary)
    
    # 按薪资排序
    top_jobs = job_data.nlargest(count, 'parsed_salary')
    
    # 准备表格数据
    table_data = top_jobs[['positionName', 'companyFullName', 'city', 'salary', 'workYear', 'education']]
    table_data.columns = ['职位名称', '公司全称', '城市', '薪资', '工作经验', '学历']
    
    # 生成描述文本
    cities = ', '.join(table_data['城市'].unique())
    companies = ', '.join(table_data['公司全称'].unique()[:3]) + ('等' if len(table_data['公司全称'].unique()) > 3 else '')
    result = f"为您找到{len(table_data)}个{skill}相关岗位，分布在{cities}等地，包括{companies}等公司。"
    
    return result, None, table_data

def get_max_salary_job(dataset, city, count=5, selected_charts=None, **kwargs):
    #获取指定城市薪资最高的岗位
    if 'salary' not in dataset.columns:
        return f"数据集缺少薪资信息，无法查询{city}薪资最高的岗位", None, None
    
    city_data = dataset[dataset['city'] == city]
    if city_data.empty:
        return f"未找到{city}的相关数据", None, None
    
    city_data['parsed_salary'] = city_data['salary'].apply(parse_salary)
    top_jobs = city_data.nlargest(count, 'parsed_salary')
    
    result = f"根据2023年拉勾网的查询结果，{city}薪资最高的{count}个岗位如下：\n"
    for idx, (_, row) in enumerate(top_jobs.iterrows(), start=1):
        result += f"\n{idx}. 岗位名称: {row['positionName']}\n"
        result += f"   平均薪资: {row['parsed_salary']:.1f}k\n"
        result += f"   公司名称: {row['companyFullName']}\n"
        result += f"   技能要求: {row['skill_list']}\n"
    
    # 准备表格数据
    table_data = top_jobs[['positionName', 'companyFullName', 'salary', 'workYear', 'education']]
    table_data.columns = ['岗位名称', '公司名称', '薪资范围', '工作年限', '学历要求']
    
    return result, None, table_data

# ==================== GLM交互函数 ====================
def chatTools(client, prompt, tools, temperature=0.6):
    response = client.chat.completions.create(
        model="glm-4",
        messages=[{"role": "user", "content": prompt}],
        tools=tools,
        tool_choice="auto",
        temperature=temperature
    )
    return response.choices[0]

def chat_glmOnce(client, prompt):
    response = client.chat.completions.create(
        model="glm-4",
        messages=[{"role": "user", "content": prompt}]
    )
    return response.choices[0]

def ask_glm(client, tools, temperature, content, function_list, dataset, selected_charts):
    ack = chatTools(client, content, tools, temperature)
    if ack.finish_reason == "tool_calls":
        tool_call = ack.message.tool_calls[0]
        function_name = tool_call.function.name
        function_args_str = tool_call.function.arguments
        try:
            function_args_dict = json.loads(function_args_str)
        except json.JSONDecodeError:
            return "参数解析失败", None, None
        else:
            if function_name in function_list:
                func = function_list[function_name]
                function_args_dict["dataset"] = dataset
                function_args_dict["prompt"] = content
                function_args_dict["selected_charts"] = selected_charts
                result, chart, table = func(**function_args_dict)
                response = chat_glmOnce(client, result)
                return response.message.content, chart, table
    return ack.message.content, None, None

# ==================== 数据可视化仪表盘 ====================
def data_dashboard(df):
    st.title("📊 招聘数据仪表盘")
    
    with st.expander("🔍 数据筛选器", expanded=True):
        col1, col2, col3 = st.columns(3)
        
        with col1:
            selected_cities = st.multiselect(
                "选择城市",
                options=df['city'].unique(),
                default=[]
            )
        
        with col2:
            company_options = df['companyFullName'].unique()
            if selected_cities:
                company_options = df[df['city'].isin(selected_cities)]['companyFullName'].unique()
            
            selected_companies = st.multiselect(
                "选择公司",
                options=company_options,
                default=[]
            )
        
        with col3:
            # 确保技能列表已处理
            valid_skills = [s for s in df['skillLables'].unique() if isinstance(s, str)]
            selected_skills = st.multiselect(
                "选择技能",
                options=valid_skills,
                default=[]
            )
        
        min_salary = st.slider(
            "最低薪资(元)",
            min_value=int(df['salary'].min()),
            max_value=int(df['salary'].max()),
            value=int(df['salary'].quantile(0.25))
        )
    
    # 应用过滤条件
    filtered_df = df.copy()
    if selected_cities:
        filtered_df = filtered_df[filtered_df['city'].isin(selected_cities)]
    if selected_companies:
        filtered_df = filtered_df[filtered_df['companyFullName'].isin(selected_companies)]
    if selected_skills:
        filtered_df = filtered_df[filtered_df['skillLables'].isin(selected_skills)]
    filtered_df = filtered_df[filtered_df['salary'] >= min_salary].reset_index(drop=True)
    
    # 可视化图表 - 使用标签页组织
    tab1, tab2, tab3, tab4, tab5 = st.tabs(["📈 薪资分析", "🎓 学历要求", "🛠️ 技能分析", "⏰ 发布时间", "🌍 地理分布"])
    
    with tab1:
        st.subheader("高薪岗位城市热图")
        fig1, ax1 = plt.subplots(figsize=(12, 8))
        high_salary = filtered_df[filtered_df['salary'] > filtered_df['salary'].quantile(0.9)]
        if not high_salary.empty:
            heatmap_data = high_salary.groupby(['city', 'industryField'])['salary'].mean().unstack()
            sns.heatmap(heatmap_data, cmap='YlOrRd', ax=ax1)
            plt.title('各城市行业薪资热图(前10%高薪)')
            st.pyplot(fig1)
        else:
            st.warning("没有足够的高薪数据生成热图")
        
        st.subheader("城市薪资分布")
        fig2, ax2 = plt.subplots(figsize=(10, 6))
        sns.boxplot(data=filtered_df, x='city', y='salary', ax=ax2)
        plt.xticks(rotation=45)
        st.pyplot(fig2)
    
    with tab2:
        st.subheader("融资阶段与学历要求")
        fig3, ax3 = plt.subplots(figsize=(10, 6))
        pd.crosstab(filtered_df['financeStage'], filtered_df['education']).plot(
            kind='bar', stacked=True, ax=ax3)
        ax3.set_xticklabels(ax3.get_xticklabels(), rotation=45)
        st.pyplot(fig3)
        
        st.subheader("工作经验与薪资关系")
        fig4, ax4 = plt.subplots(figsize=(10, 6))
        sns.boxplot(data=filtered_df, x='workYear', y='salary', ax=ax4)
        plt.xticks(rotation=45)
        st.pyplot(fig4)
    
    with tab3:
        st.subheader("行业领域技能词云")
        try:
            # 确保所有技能为字符串并过滤空值
            filtered_df['skillLables'] = filtered_df['skillLables'].fillna('').astype(str)
            industry_skills = filtered_df[filtered_df['skillLables'] != ''].groupby('industryField')['skillLables'].apply(
                lambda x: ' '.join(x)
            )
            
            if not industry_skills.empty:
                wordcloud = WordCloud(
                    width=800, 
                    height=400, 
                    background_color='white',
                    font_path='simhei.ttf',
                    collocations=False
                ).generate(' '.join(industry_skills))
                
                fig5, ax5 = plt.subplots(figsize=(12, 8))
                ax5.imshow(wordcloud, interpolation='bilinear')
                ax5.axis('off')
                st.pyplot(fig5)
            else:
                st.warning("没有足够的技能数据生成词云")
        except Exception as e:
            st.warning(f"无法生成词云: {str(e)}")
        
        if len(selected_skills) > 0:
            st.subheader("技能地理城市薪资分布")
            fig6, ax6 = plt.subplots(figsize=(14, 8))
            skill_city_salary = filtered_df.groupby(['city', 'skillLables'])['salary'].mean().unstack()
            
            for skill in selected_skills:
                if skill in skill_city_salary.columns:
                    sns.lineplot(data=skill_city_salary[skill], marker='o', label=skill, ax=ax6)
            
            plt.xticks(rotation=45)
            plt.title(f'"{",".join(selected_skills)}"技能在各城市的平均薪资')
            plt.ylabel('平均薪资(元)')
            plt.legend()
            st.pyplot(fig6)
    
    with tab4:
        st.subheader("招聘时间模式分析")
        fig7, ax7 = plt.subplots(figsize=(12, 6))
        hour_pattern = filtered_df.groupby(['industryField', 'createHour']).size().unstack()
        sns.heatmap(hour_pattern, cmap='YlGnBu', ax=ax7)
        plt.title('各行业招聘发布时间规律')
        st.pyplot(fig7)
        
        st.subheader("每日招聘发布趋势")
        filtered_df['createDate'] = pd.to_datetime(filtered_df['createTime']).dt.date
        daily_trend = filtered_df.groupby('createDate').size()
        fig8, ax8 = plt.subplots(figsize=(12, 6))
        sns.lineplot(data=daily_trend, ax=ax8)
        plt.title('每日招聘发布数量趋势')
        st.pyplot(fig8)
    
    with tab5:
        st.subheader("城市招聘热度分布")
        city_counts = filtered_df['city'].value_counts().reset_index()
        city_counts.columns = ['city', 'count']
        fig9, ax9 = plt.subplots(figsize=(12, 8))
        sns.barplot(data=city_counts, x='count', y='city', ax=ax9)
        plt.title('各城市招聘岗位数量')
        st.pyplot(fig9)
        
        st.subheader("行业领域城市分布")
        industry_city = filtered_df.groupby(['industryField', 'city']).size().unstack().fillna(0)
        fig10, ax10 = plt.subplots(figsize=(14, 8))
        sns.heatmap(industry_city, cmap='YlOrBr', ax=ax10)
        plt.title('各行业在不同城市的分布情况')
        st.pyplot(fig10)

# ==================== 智能查询系统 ====================
def smart_query(df):
    st.title("🔍 智能查询系统")
    
    query_type = st.selectbox(
        "选择查询类型",
        options=[
            "按技能查询岗位", 
            "按岗位查询技能", 
            "查询高薪岗位", 
            "查询技能关联性",
            "查询城市薪资最高岗位"
        ]
    )
    
    if query_type == "按技能查询岗位":
        skill = st.text_input("输入技能名称", "Python")
        if st.button("查询"):
            # 确保技能列已处理
            df['skillLables'] = df['skillLables'].fillna('').astype(str)
            skill_df = df[df['skillLables'].str.contains(skill, case=False, na=False)]
            if not skill_df.empty:
                st.subheader(f"需要'{skill}'技能的岗位")
                st.dataframe(
                    skill_df[['positionName', 'companyFullName', 'city', 'salary', 'workYear', 'education']]
                    .sort_values('salary', ascending=False)
                    .drop_duplicates(),
                    column_config={
                        "salary": st.column_config.NumberColumn("薪资", format="%.0f元")
                    }
                )
                
                st.subheader("按城市分布")
                city_counts = skill_df['city'].value_counts().reset_index()
                city_counts.columns = ['城市', '岗位数量']
                st.bar_chart(city_counts.set_index('城市'))
            else:
                st.warning(f"没有找到需要'{skill}'技能的岗位")
    
    elif query_type == "按岗位查询技能":
        position = st.text_input("输入岗位名称", "Java工程师")
        if st.button("查询"):
            position_df = df[df['positionName'].str.contains(position, case=False)]
            if not position_df.empty:
                st.subheader(f"'{position}'岗位的技能要求")
                
                # 确保技能列已处理
                position_df['skillLables'] = position_df['skillLables'].fillna('').astype(str)
                skills = position_df[position_df['skillLables'] != '']['skillLables'].value_counts().reset_index()
                skills.columns = ['技能', '出现次数']
                
                fig, ax = plt.subplots(figsize=(10, 6))
                sns.barplot(data=skills.head(10), x='技能', y='出现次数', ax=ax)
                plt.xticks(rotation=45)
                st.pyplot(fig)
                
                st.dataframe(skills)
            else:
                st.warning(f"没有找到'{position}'相关的岗位")
    
    elif query_type == "查询高薪岗位":
        threshold = st.slider("选择薪资百分位", 90, 99, 95)
        if st.button("查询"):
            high_salary = df[df['salary'] > df['salary'].quantile(threshold/100)]
            st.subheader(f"薪资前{100-threshold}%的高薪岗位")
            st.dataframe(
                high_salary[['positionName', 'companyFullName', 'city', 'salary', 'workYear', 'education']]
                .sort_values('salary', ascending=False)
                .drop_duplicates(),
                column_config={
                    "salary": st.column_config.NumberColumn("薪资", format="%.0f元")
                }
            )
    
    elif query_type == "查询技能关联性":
        skill = st.text_input("输入技能名称", "Python")
        
        # 添加参数调节选项
        col1, col2 = st.columns(2)
        with col1:
            min_support = st.slider("最小支持度", 0.1, 0.9, 0.5, 0.05)
        with col2:
            min_threshold = st.slider("最小阈值", 1.0, 3.0, 2.0, 0.1)
        
        if st.button("查询"):
            try:
                advisor = CareerAdvisor(df)
                
                # 使用用户设置的参数重新生成规则
                advisor._generate_association_rules(
                    min_support=min_support,
                    min_threshold=min_threshold
                )
                
                related_skills = advisor.get_related_skills(skill)
                
                if related_skills:
                    st.subheader(f"与'{skill}'关联的技能")
                    
                    # 显示关联技能表格
                    skill_df = pd.DataFrame(related_skills, columns=['技能', '关联度'])
                    st.dataframe(
                        skill_df.sort_values('关联度', ascending=False),
                        column_config={
                            "关联度": st.column_config.ProgressColumn(
                                format="%.2f",
                                min_value=0,
                                max_value=skill_df['关联度'].max()
                            )
                        }
                    )
                    
                    # 可视化
                    fig, ax = plt.subplots(figsize=(10, 6))
                    sns.barplot(data=skill_df, x='关联度', y='技能', ax=ax)
                    plt.title(f"与'{skill}'关联的技能")
                    st.pyplot(fig)
                else:
                    st.warning(f"没有找到与'{skill}'关联的技能")
                    
            except Exception as e:
                st.error(f"分析失败: {str(e)}")
                st.info("""
                可能原因:
                1. 数据量过大导致内存不足，请尝试提高最小支持度/阈值
                2. 该技能在数据中出现频率过低
                3. 系统资源限制，请稍后再试
                """)
    
    elif query_type == "查询城市薪资最高岗位":
        city = st.selectbox("选择城市", df['city'].unique())
        if st.button("查询"):
            city_df = df[df['city'] == city]
            top_jobs = city_df.nlargest(5, 'salary')
            
            st.subheader(f"{city}薪资最高的5个岗位")
            st.dataframe(
                top_jobs[['positionName', 'companyFullName', 'salary', 'workYear', 'education']],
                column_config={
                    "salary": st.column_config.NumberColumn("薪资", format="%.0f元")
                }
            )
            
            fig, ax = plt.subplots(figsize=(10, 6))
            sns.barplot(data=top_jobs, x='positionName', y='salary', ax=ax)
            plt.xticks(rotation=45)
            plt.title(f"{city}薪资最高的岗位")
            st.pyplot(fig)

# ==================== 职业规划工具 ====================
def career_advisor(df):
    st.title("🧭 职业规划工具")
    
    advisor = CareerAdvisor(df)
    
    tool_type = st.selectbox(
        "选择工具类型",
        options=[
            "根据技能推荐岗位", 
            "根据岗位查询所需技能", 
            "根据技能推荐关联技能",
            "查询技能与岗位关联性",
            "查询技能间关联性"
        ]
    )
    
    if tool_type == "根据技能推荐岗位":
        skills = st.text_input("输入您掌握的技能(多个技能用逗号分隔)", "Python,SQL")
        if st.button("分析"):
            skill_list = [s.strip() for s in skills.split(',') if s.strip()]
            positions = advisor.get_positions_for_skills(skill_list)
            
            if positions:
                st.subheader("推荐岗位")
                for pos, score in positions:
                    st.write(f"- {pos} (匹配度: {score*100:.1f}%)")
                
                pos_df = pd.DataFrame(positions, columns=['岗位', '匹配度'])
                fig, ax = plt.subplots(figsize=(10, 6))
                sns.barplot(data=pos_df, x='匹配度', y='岗位', ax=ax)
                plt.title("技能与岗位匹配度")
                st.pyplot(fig)
                st.subheader("📈 职业发展建议")
                primary_skill = skill_list[0]  # 取用户最核心的技能
                advice = f"""基于您掌握的 {', '.join(skill_list)} 技能：
                1. **短期目标**：优先申请【{positions[0][0]}】岗位（当前匹配度最高）
                2. **技能组合建议**：
                    - 搭配学习 {positions[0][0]} 岗位最常要求的其他技能（如：{advisor.get_top_3_skills(positions[0][0])}）
                    - 与{primary_skill}强关联的技能：{advisor.get_top_related(primary_skill)}
                3. **长期发展**：
                    - 横向发展：考虑向【{positions[-1][0]}】等关联岗位拓展
                    - 纵向晋升：{primary_skill}专家路线 → 技术总监/架构师"""
                st.markdown(advice)
            else:
                st.warning("没有找到匹配的岗位")
    
    elif tool_type == "根据岗位查询所需技能":
        position = st.text_input("输入目标岗位名称", "算法工程师")
        if st.button("分析"):
            skills = advisor.get_skills_for_position(position)
            
            if skills:
                st.subheader(f"'{position}'岗位所需技能")
                for skill, count in skills:
                    st.write(f"- {skill} (出现次数: {count})")
                
                skill_df = pd.DataFrame(skills, columns=['技能', '出现次数'])
                fig, ax = plt.subplots(figsize=(10, 6))
                sns.barplot(data=skill_df.head(10), x='出现次数', y='技能', ax=ax)
                plt.title("岗位技能需求")
                st.pyplot(fig)
                st.subheader("🚀 岗位成长路线")
                roadmap = f"""成为优秀的 {position} 需要：
                **初级阶段**：
                - 核心：掌握 {skills[0][0]}（基础必备）
                - 配套：学习 {skills[1][0]} 和 {skills[2][0]}（高频组合技能）
                **中级阶段**：
                - 深化：{advisor.get_advanced_skills(position)} 等进阶技术
                - 实践：参与相关的项目
                **高级阶段**：
                - 拓展：了解行业领域知识
                - 管理：团队协作和项目管理能力"""
                st.markdown(roadmap)
            else:
                st.warning("没有找到该岗位的技能信息")
    
    elif tool_type == "根据技能推荐关联技能":
        skill = st.text_input("输入您擅长的技能", "Python")
        if st.button("分析"):
            related_skills = advisor.get_related_skills(skill)
            
            if related_skills:
                st.subheader(f"与'{skill}'关联的技能")
                for s, count in related_skills:
                    st.write(f"- {s} (共同出现次数: {count})")
                
                skill_df = pd.DataFrame(related_skills, columns=['技能', '共同出现次数'])
                fig, ax = plt.subplots(figsize=(10, 6))
                sns.barplot(data=skill_df, x='共同出现次数', y='技能', ax=ax)
                plt.title("技能关联性分析")
                st.pyplot(fig)
                st.subheader("🌱 技能发展路径")
                path = f"""您的核心技能 **{skill}** 可以延伸出：
                            1. **技术纵深路线**：
                            {skill} → {related_skills[0][0]} → 该领域的更高级技能
                            （适合走技术专家路线）
                            2. **横向扩展路线**：
                            {skill} + {related_skills[1][0]} → 转向相关交叉岗位
                            （适合多元化发展）
                            3. **新兴领域结合**：
                            将{skill}应用于新兴热门领域
                            （前沿方向，竞争较少）"""
                st.markdown(path)
            else:
                st.warning("没有找到关联的技能")
    
    elif tool_type == "查询技能与岗位关联性":
        col1, col2 = st.columns(2)
        with col1:
            skill = st.text_input("输入技能名称", "C++")
        with col2:
            position = st.text_input("输入岗位名称", "算法工程师")
        
        if st.button("分析"):
            score = advisor.query_skill_position_association(skill, position)
            st.subheader("技能与岗位关联性分析")
            
            if score >= 0.8:
                st.success(f"技能 '{skill}' 与岗位 '{position}' 高度相关 (评分: {score:.2f})")
                st.markdown("""
                **职业发展建议:**
                - 该技能是该岗位的核心要求
                - 建议重点提升该技能的深度和实战应用能力
                - 可以寻找需要该技能的专项岗位
                """)
            elif score >= 0.5:
                st.info(f"技能 '{skill}' 与岗位 '{position}' 中度相关 (评分: {score:.2f})")
                st.markdown("""
                **职业发展建议:**
                - 该技能是该岗位的有益补充
                - 建议结合其他核心技能一起发展
                - 可以作为差异化竞争优势
                """)
            else:
                st.warning(f"技能 '{skill}' 与岗位 '{position}' 关联性较低 (评分: {score:.2f})")
                st.markdown("""
                **职业发展建议:**
                - 该技能不是该岗位的主要要求
                - 建议学习该岗位更相关的核心技能
                - 可以考虑其他更适合该技能的岗位方向
                """)
    
    elif tool_type == "查询技能间关联性":
        col1, col2 = st.columns(2)
        with col1:
            skill1 = st.text_input("输入第一个技能", "Python")
        with col2:
            skill2 = st.text_input("输入第二个技能", "机器学习")
        
        if st.button("分析"):
            score = advisor.query_skill_skill_association(skill1, skill2)
            st.subheader("技能间关联性分析")
            
            if score >= 2.0:
                st.success(f"技能 '{skill1}' 和 '{skill2}' 高度关联 (关联度: {score:.2f})")
                st.markdown("""
                **学习建议:**
                - 这两个技能经常在同一个岗位中同时要求
                - 建议同时学习和掌握这两个技能
                - 可以寻找结合这两个技能的项目实践机会
                """)
            elif score >= 1.5:
                st.info(f"技能 '{skill1}' 和 '{skill2}' 中度关联 (关联度: {score:.2f})")
                st.markdown("""
                **学习建议:**
                - 这两个技能有一定的协同效应
                - 建议在掌握核心技能后学习另一个
                - 可以关注同时需要这两个技能的复合型岗位
                """)
            else:
                st.warning(f"技能 '{skill1}' 和 '{skill2}' 关联性较低 (关联度: {score:.2f})")
                st.markdown("""
                **学习建议:**
                - 这两个技能通常不会同时要求
                - 建议根据职业方向选择其中一个重点发展
                - 可以分别寻找适合每个技能的专项岗位
                """)

# ==================== AI对话助手 ====================
def init_ai_client(api_key):
    return ZhipuAI(api_key=api_key)
def ai_chatbot(df):
    st.title("💬 AI招聘助手")
    
    if "api_key" not in st.session_state:
        st.session_state.api_key = ""
    
    if "messages" not in st.session_state:
        st.session_state.messages = [
            {"role": "assistant", "content": "您好，我是招聘数据分析助手，请问有什么可以帮您？"}
        ]
    
    with st.sidebar:
        st.title('配置')
        if 'API_TOKEN' in st.session_state and len(st.session_state['API_TOKEN']) > 1:
            st.success('API Token已经配置', icon='✅')
            key = st.session_state['API_TOKEN']
        else:
            key = ""
        key = st.text_input('输入Token:', type='password', value=key, placeholder="请输入您的API Token")
        st.session_state['API_TOKEN'] = key
        
        model = st.selectbox("选择模型", ["glm-4"])
        max_tokens = st.slider("max_tokens", 0, 2000, value=512)
        temperature = st.slider("temperature", 0.0, 2.0, value=0.6)
        
        st.title('可视化选项')
        chart_options = ["柱状图", "饼图", "折线图", "表格"]
        selected_charts = st.multiselect("选择要显示的图表类型", chart_options, default=["表格"])

    dataset = load_data()
    if dataset.empty:
        st.error("数据加载失败，请检查数据文件！")
        return
    
    if "messages" not in st.session_state:
        st.session_state.messages = [{"role": "assistant", "content": "你好，我是招聘数据助手，有什么可以帮助你的？"}]

    for message in st.session_state.messages:
        with st.chat_message(message["role"]):
            st.write(message["content"])

    def clear_chat_history():
        st.session_state.messages = [{"role": "assistant", "content": "你好，我是招聘数据助手，有什么可以帮助你的？"}]
    st.sidebar.button('清空聊天记录', on_click=clear_chat_history)

    if len(key) > 1:
        if prompt := st.chat_input("请输入您的问题："):
            st.session_state.messages.append({"role": "user", "content": prompt})
            with st.chat_message("user"):
                st.write(prompt)
            
            with st.chat_message("assistant"):
                with st.spinner("思考中..."):
                    client = init_ai_client(key)
                    
                    advisor = CareerAdvisor(df)
                    
                    tools = [
                        {
                            "type": "function",
                            "function": {
                                "name": "get_asso_skill",
                                "description": "根据用户提示的技能，找出关联的技能用于学习推荐",
                                "parameters": {
                                    "type": "object",
                                    "properties": {
                                        "skill": {"type": "string", "description": "技能名称，如java，python，c++"},
                                    },
                                    "required": ["skill"],
                                },
                            }
                        },
                        {
                            "type": "function",
                            "function": {
                                "name": "cacu_postion_skill_wordcount",
                                "description": "根据用户提示的岗位名称或者工作内容，找出学习或者技能要求",
                                "parameters": {
                                    "type": "object",
                                    "properties": {
                                        "postionName": {"type": "string", "description": "岗位名称，如java工程师，嵌入式开发"},
                                    },
                                    "required": ["postionName"],
                                },
                            }
                        },
                        {
                            "type": "function",
                            "function": {
                                "name": "cacu_skill_position_wordcount",
                                "description": "根据用户给出的技能，查找匹配的岗位",
                                "parameters": {
                                    "type": "object",
                                    "properties": {
                                        "skill": {"type": "string", "description": "技术名，如java,C,C++,linux等"},
                                    },
                                    "required": ["skill"],
                                },
                            }
                        },
                        {
                            "type": "function",
                            "function": {
                                "name": "get_max_salary_job",
                                "description": "查询某个城市薪资最高的岗位",
                                "parameters": {
                                    "type": "object",
                                    "properties": {
                                        "city": {"type": "string", "description": "城市名称，如北京，上海"},
                                    },
                                    "required": ["city"],
                                },
                            }
                        },
                        {
                            "type": "function",
                            "function": {
                                "name": "get_job_recommendations",
                                "description": "根据用户给出的技能，查找相关的招聘岗位信息",
                                "parameters": {
                                    "type": "object",
                                    "properties": {
                                        "skill": {"type": "string", "description": "技能名称，如java,python等"},
                                    },
                                    "required": ["skill"],
                                },
                            }
                        }
                    ]
                    function_list = {
                        "get_asso_skill": get_asso_skill,#根据输入的技能，返回关联性最高的其他技能（用于学习推荐）
                        "cacu_postion_skill_wordcount": cacu_postion_skill_wordcount,#分析特定岗位的技能需求频率（如"Java工程师需要哪些技能？")
                        "cacu_skill_position_wordcount": cacu_skill_position_wordcount,#根据技能推荐匹配的岗位（如"会Python能找什么工作？")
                        "get_max_salary_job": get_max_salary_job,#查询某城市薪资最高的岗位（如"上海薪资最高的5个岗位")
                        "get_job_recommendations": get_job_recommendations#根据技能推荐具体的招聘岗位信息（如"会机器学习有哪些岗位？")
                    }
                    response, chart, table = ask_glm(client, tools, temperature, prompt, function_list, dataset, selected_charts)
                    st.write(response)
                    
                    # 显示表格
                    if table is not None and "表格" in selected_charts:
                        st.dataframe(table.style.format({
                            '薪资': lambda x: f"{x}k",
                            '平均薪资(k)': lambda x: f"{x:.1f}k"
                        }), use_container_width=True)
                    
                    # 显示图表
                    if chart is not None:
                        if isinstance(chart, list):
                            for c in chart:
                                if hasattr(c, 'get_options'):
                                    st_pyecharts(c)
                        elif hasattr(chart, 'get_options'):
                            st_pyecharts(chart)
                    
                    st.session_state.messages.append({"role": "assistant", "content": response})

# ==================== 主函数 ====================
def main():
    if not login_page():
        return
    
    df = load_data()
    if df.empty:
        st.error("无法加载数据，请检查数据文件!")
        return

    st.sidebar.title("导航")
    app_mode = st.sidebar.radio(
        "选择功能模块",
        ["📊 数据仪表盘", "🔍 智能查询", "🧭 职业规划", "💬 AI助手"]
    )

    if app_mode == "📊 数据仪表盘":
        data_dashboard(df)
    elif app_mode == "🔍 智能查询":
        smart_query(df)
    elif app_mode == "🧭 职业规划":
        career_advisor(df)
    elif app_mode == "💬 AI助手":
        ai_chatbot(df)

if __name__ == "__main__":
    main()